/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package cz.mzk.kramerius.rukopisyimport.foxml;

import cz.mzk.kramerius.commons.DatastreamId;
import cz.mzk.kramerius.commons.Namespaces;
import cz.mzk.kramerius.imports.Datastream;
import cz.mzk.kramerius.imports.DatastreamFactory;
import cz.mzk.kramerius.imports.FoxmlHelper;
import cz.mzk.kramerius.imports.Policy;
import java.io.File;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.Namespace;
import org.dom4j.QName;

/**
 *
 * @author Martin Rehanek (rehan@mzk.cz)
 */
public class InternalPartBuilder {

    private static final Logger logger = Logger.getLogger(InternalPartBuilder.class.getName());
    private final Pages pages;
    private final File conversionDir;

    InternalPartBuilder(Pages pages, File conversionDir) {
        this.pages = pages;
        this.conversionDir = conversionDir;
    }

    public InternalPart createInternalPart(String uuid, String pageFrom, String pageTo, String note, String title) throws Exception {
        InternalPart internalPart = new InternalPart(FoxmlHelper.generateUuid(), title, pageFrom, pageTo, note);
        Datastream dc = createDcDatastream(internalPart, Policy.PUBLIC);
        internalPart.addDatastream(dc);
        Datastream mods = createModsDatastream(internalPart, pages);
        internalPart.addDatastream(mods);
        Datastream relsExt = createRelsExtDatastream(internalPart, pages);
        internalPart.addDatastream(relsExt);
        Datastream policy = FoxmlHelper.createPolicyDatastream(Policy.PUBLIC);
        internalPart.addDatastream(policy);
        FoxmlHelper.saveFoxml(conversionDir, internalPart);
        return internalPart;
    }

    private Datastream createDcDatastream(InternalPart internalPart, Policy policy) throws Exception {
        Element rootElement = DocumentHelper.createElement(new QName("dc", Namespaces.oai_dc));
        rootElement.add(Namespaces.dc);
        Document doc = DocumentHelper.createDocument(rootElement);
        Element title = rootElement.addElement(new QName("title", Namespaces.dc));
        title.addText(internalPart.getTitle());
        Element identifier = rootElement.addElement(new QName("identifier", Namespaces.dc));
        identifier.setText(internalPart.getPid());
        Element type = rootElement.addElement(new QName("type", Namespaces.dc));
        type.addText("model:internalpart");
        Element rights = rootElement.addElement(new QName("rights", Namespaces.dc));
        rights.addText("policy:" + policy.toString().toLowerCase());
        return DatastreamFactory.createInlineDatastream(doc, DatastreamId.DC);
    }

    private Datastream createModsDatastream(InternalPart internalPart, Pages pages) {
        Element modsCollection = FoxmlHelper.createModsCollectionEl();
        Namespace modsNs = Namespaces.mods;
        Element mods = modsCollection.addElement(new QName("mods", modsNs));
        mods.addAttribute("version", "3.3");
        Element idUrn = mods.addElement(new QName("identifier", modsNs));
        idUrn.addAttribute("type", "urn");
        idUrn.addText(internalPart.getUuid());

        Element idSici = mods.addElement(new QName("identifier", modsNs));
        idSici.addAttribute("type", "sici");

        Element titleInfo = mods.addElement(new QName("titleInfo", modsNs));
        Element title = titleInfo.addElement(new QName("title", modsNs));
        title.addText(internalPart.getTitle());
//        Element subTitle = titleInfo.addElement(new QName("subTitle", modsNs));

//        Element titleInfoAlt = mods.addElement(new QName("titleInfo", modsNs));
//        titleInfoAlt.addAttribute("type", "alternative");
//        Element titleAlt = titleInfoAlt.addElement(new QName("title", modsNs));

//        Element subject = mods.addElement(new QName("subject", modsNs));
//        Element topic = subject.addElement(new QName("topic", modsNs));

//        Element language = mods.addElement(new QName("language", modsNs));
//        Element languageTerm = language.addElement(new QName("languageTerm", modsNs));
//        languageTerm.addAttribute("authority", "iso639-2b");
//        languageTerm.addAttribute("type", "code");
//        languageTerm.addText("TODO");

        Element physicalDescription = mods.addElement(new QName("physicalDescription", modsNs));
        if (internalPart.getNote() != null && !internalPart.getNote().equals("")) {
            Element note = physicalDescription.addElement(new QName("note", modsNs));
            note.addText(internalPart.getNote());
        }

        Element classificationDdc = mods.addElement(new QName("classification", modsNs));
        classificationDdc.addAttribute("authority", "ddc");
        Element classificationUdc = mods.addElement(new QName("classification", modsNs));
        classificationUdc.addAttribute("authority", "udc");

        Element part = mods.addElement(new QName("part", modsNs));
        part.addAttribute("type", "Chapter");
        //TODO: zmenit typ. Typ je tezko urcitelny z originalnich metadat v tei5
        Element extent = part.addElement(new QName("extent", modsNs));
        extent.addAttribute("unit", "pages");

        Element start = extent.addElement(new QName("start", modsNs));
        String pageFrom = pages.getPageById(internalPart.getPageFrom()).getLabel();
        start.addText(pageFrom);

        Element end = extent.addElement(new QName("end", modsNs));
        String pageTo = pages.getPageById(internalPart.getPageTo()).getLabel();
        end.addText(pageTo);

        Element total = extent.addElement(new QName("total", modsNs));
        String pageNumber = Integer.toString(pages.size());
        total.addText(pageNumber);

        Element list = extent.addElement(new QName("list", modsNs));
        StringBuilder pageListing = new StringBuilder();
        pageListing.append("s. ").append(pageFrom).append(" - ").append(pageTo);
        list.addText(pageListing.toString());

        Element detail = part.addElement(new QName("detail", modsNs));
        detail.addAttribute("type", "pageNumber");
        Element number = detail.addElement(new QName("number", modsNs));
        number.addText(pageFrom);

        Element accessCondition = mods.addElement(new QName("accessCondition", modsNs));
        accessCondition.addAttribute("type", "restrictionOnAccess");
        Document doc = DocumentHelper.createDocument(modsCollection);
        return DatastreamFactory.createInlineDatastream(doc, DatastreamId.BIBLIO_MODS);
    }

    private Datastream createRelsExtDatastream(InternalPart internalPart, Pages pages) {
        Document doc = FoxmlHelper.createRelsExtSkeleton(internalPart, "internalpart", Policy.PUBLIC);
        FoxmlHelper.addElementToRelsExt(doc, "pageFrom", null, String.valueOf(internalPart.getPageFrom()));
        FoxmlHelper.addElementToRelsExt(doc, "pageTo", null, String.valueOf(internalPart.getPageTo()));
        addPageReferrences(internalPart, pages, doc);
        return DatastreamFactory.createInlineDatastream(doc, DatastreamId.RELS_EXT);
    }

    private void addPageReferrences(InternalPart internalPart, Pages pages, Document relsExt) {
        //Page firstPage = pages.getPageByNumber(internalPart.getPageFrom());
        Page firstPage = pages.getPageById(internalPart.getPageFrom());
        if (firstPage == null) {
            throw new IllegalArgumentException("cannot find first page for internal part " + internalPart.getLabel());
        }
        addPageReferrence(internalPart, firstPage, pages, relsExt);
    }

    private void addPageReferrence(InternalPart internalPart, Page page, Pages pages, Document relsExt) {
        internalPart.addPagePid(page.getPid());
        FoxmlHelper.addRelationshipToRelsExt(relsExt, page, "isOnPage");
        //System.err.println("adding referrence to page " + page);
        if (isLastPageInMonograph(page, pages)) {
            logger.log(Level.WARNING, "last page in monograph but no end of internal part found ");
        } else if (isLastPageOfInternalPart(page, internalPart)) {
            //System.err.println("last page of internal part");
        } else {
            Page newPage = pages.nextPage(page);
            //System.err.println("next page");
            addPageReferrence(internalPart, newPage, pages, relsExt);
        }
    }

    private boolean isLastPageInMonograph(Page page, Pages pages) {
        Page nextPage = pages.nextPage(page);
        return nextPage == null;
    }

    private boolean isLastPageOfInternalPart(Page page, InternalPart internalPart) {
        return internalPart.getPageTo().equals(page.getId());
    }
}
